/*
 *  Axolotl - Alternate persistence for Grails
 *  Copyright (C) 2008  Juanjo García Latre
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package com.googlecode.axolotl.query;

import java.io.Serializable;


/**
 * Describes an entity's property in the context of a query. <code>PropertyName</code>s can be
 * chained in order to access properties in related entities. 
 * 
 * For example: suppose we have the entities <code>Author</code>, <code>Book</code> and <code>Edition</code>. 
 * If we are looking for authors who published books in a given year, we would write something like:
 * <code>
 * 	PropertyName year = new PropertyName( new PropertyName( new PropertyName("books"), "editions"), "year" )
 * </code>
 * or more concisely:
 * <code>
 * 	PropertyName year = new PropertyName( "books.editions.year" )
 * </code>
 * If we are querying editions and are interested in the author:
 * <code>
 * 	PropertyName authorName = new PropertyName( "book.author.name" )
 * </code>
 * 
 * @author     juanjo
 */
public class PropertyName implements Serializable, Cloneable {

	public static final char NAME_SEPARATOR = '.';

	private PropertyName parent;
	private String name;
	private Class narrowType;


	/**
	 * 
	 */
	public PropertyName() {
	}


	/**
	 *
	 */
	public PropertyName(String name) {
		int pos = name.lastIndexOf(NAME_SEPARATOR);

		if (pos >= 0) {
			this.parent = new PropertyName( name.substring(0, pos) );
			this.name = name.substring(pos + 1);         
		}
		else {
			this.parent = null;
			this.name = name;
		}
	}


	/** 
	 * 
	 */
	public PropertyName(String name, Class narrowType) {
		this(name);
		this.narrowType = narrowType;
	}


	/**
	 *
	 */
	public PropertyName(PropertyName parent, String name) {
		int pos = name.lastIndexOf(NAME_SEPARATOR);

		if (pos >= 0) {
			this.parent = new PropertyName( parent, name.substring(0, pos) );
			this.name = name.substring(pos + 1);         
		}
		else {
			this.parent = parent;
			this.name = name;
		}
	}


	/**
	 *
	 */
	public PropertyName(PropertyName parent, String name, Class narrowType) {
		this(parent, name);
		this.narrowType = narrowType;
	}


	/**
	 * 
	 */
	public Object clone() {
		try	{
			PropertyName propertyName = (PropertyName) super.clone();
			propertyName.parent = this.parent;
			propertyName.narrowType = this.narrowType;
			return propertyName;
		}
		catch (CloneNotSupportedException e) {
			return null;
		}
	}


	/**
	 *
	 */
	public String getName() {
		return name;
	}


	/**
	 * 
	 */
	public void setName(String name) {
		this.name = name;
	}


	/**
	 *
	 */
	public PropertyName getParent() {
		return parent;
	}


	/**
	 * 
	 */
	public void setParent(PropertyName parent) {
		this.parent = parent;
	}


	/**
	 * 
	 */
	public Class getNarrowType() {
		return narrowType;
	}


	/**
	 * 
	 */
	public void setNarrowType(Class narrowType) {
		this.narrowType = narrowType;
	}


	/**
	 *
	 */
	public String getCanonicalName() {
		String c = name;

		if (getParent() != null) {
			c = getParent().getCanonicalName() + NAME_SEPARATOR + c;
		}
		return c;
	}


	/**
	 *
	 */
	public <R> R accept(QueryVisitor<R> visitor) {
		return visitor.visit(this);
	}


	/**
	 *
	 */
	public boolean equals(Object o) {
		if (o == null || !(o instanceof PropertyName)) {
			return false;
		}
		else {
			PropertyName that = (PropertyName) o;
			return (that.parent == null ? this.parent == null : that.parent.equals(this.parent)) &&
			(that.name == null ? this.name == null : that.name.equals(this.name)) &&
			(that.narrowType == null ? this.narrowType == null : that.narrowType.equals(this.narrowType)); 
		}
	}


	/**
	 *
	 */
	public String toString() {
		return accept(Criterion.STRING_RENDERER).toString();
	}

}
